﻿using System;
using System.Collections.Generic;
using __Able.Network.Events;
using __Able.Network.Packets;
using __Able.Network.Threading;
using __Able.Network.RequestPacket;

namespace __Able.Network.Manager
{
    public class PacketManager
    {
        public Channel channel { get; private set; }

        private Queue<_InPacket> receiveQueue;
        private PacketIncomeTrigger packetIncomTrigger;
        private ResendTrigger resendTrigger;
        private KeepAliveTrigger keepAliveTrigger;
        private ProcessorRouter router;

        /// <summary>
        /// </summary>
        /// <param name="client">The client.</param>
        internal PacketManager(Channel channel)
        {
            router = new ProcessorRouter(channel);
            router.InstallProcessor(new BasicFamilyProcessor(channel));

            this.channel = channel;
            receiveQueue = new Queue<_InPacket>();

            SetupTrigger();
        }
        internal PacketManager() { }

        /// <summary>
        /// Setups the trigger.
        /// </summary>
        private void SetupTrigger()
        {
            this.packetIncomTrigger = new PacketIncomeTrigger(this.channel);
            this.resendTrigger = new ResendTrigger(this.channel);
            this.keepAliveTrigger = new KeepAliveTrigger(this.channel);
        }
        /// <summary>
        /// Setdowns the trigger.
        /// </summary>
        internal void SetdownTrigger()
        {
            ThreadExcutor.UnRegisterIntervaluObject(resendTrigger);
        }

        #region 输入包处理

        /// <summary>
        /// 添加输入包
        /// </summary>
        /// <param name="inPacket">The in packet.</param>
        public void AddIncomingPacket(_InPacket inPacket, string portName)
        {
            lock (receiveQueue)
            {
                if (inPacket == null)
                {
                    return;
                }
                inPacket.PortName = portName;
                receiveQueue.Enqueue(inPacket);
                ThreadExcutor.Submit(this.packetIncomTrigger, this);
            }
        }
        /// <summary>
        /// 从接收队列中得到第一个包，并且把这个包从队列中移除
        /// </summary>
        /// <returns></returns>
        public _InPacket RemoveIncomingPacket()
        {
            if (receiveQueue.Count == 0)
            {
                return null;
            }
            return receiveQueue.Dequeue();
        }
        /// <summary>
        /// 收到服务器确认
        /// 删除一个重发包
        /// </summary>
        /// <param name="packet">The packet.</param>
        public void RemoveResendPacket(_InPacket packet)
        {
            resendTrigger.Remove(packet);
        }
        /// <summary>
        /// 通知包处理器包到达事件
        /// </summary>
        /// <param name="inPacket">The in packet.</param>
        public void FirePacketArrivedEvent(_InPacket inPacket)
        {
            router.PacketArrived(inPacket);
        }
        /// <summary>
        /// 添加重发包
        /// </summary>
        /// <param name="outPacket">The out packet.</param>
        public void AddResendPacket(_OutPacket outPacket, string portName)
        {
            resendTrigger.Add(outPacket, portName);
        }
        #endregion

        #region 发送包
        /// <summary>
        /// 通用方法，发送一个packet
        /// 这个方法用在一些包构造比较复杂的情况下，比如上传分组信息这种包，
        /// 包中数据的来源是无法知道的也不是用几个参数就能概括的，可能也和实现有关。
        /// </summary>
        /// <param name="packet">The packet.</param>
        public void SendPacket(_OutPacket packet)
        {
            string portName = "";
            SendPacket(packet, portName);
        }
        /// <summary>
        /// 通过指定port发送一个包
        /// </summary>
        /// <param name="packet">The packet.</param>
        /// <param name="port">The port.</param>
        public void SendPacket(_OutPacket packet, string port)
        {
            SendPacket(packet, port, false);
        }
        /// <summary>
        /// 通过指定port发送一个包
        /// </summary>
        /// <param name="packet">The packet.</param>
        /// <param name="port">The port.</param>
        /// <param name="monitor">if set to <c>true</c> [monitor].</param>
        public void SendPacket(_OutPacket packet, string port, bool monitor)
        {
            if (channel.ConnectionManager.EnsureConnection(port, true))
            {
                channel.ConnectionManager.ConnectionPool.Send(port, packet, monitor);
            }
            else
            {
                OnLostConnection(new EventArgs<_OutPacket>(packet));
            }
        }
        /// <summary>
        /// 不管有没有登录，都把包发出去
        /// </summary>
        /// <param name="packet">The packet.</param>
        /// <param name="port">The port.</param>
        public void SendPacketAnyway(_OutPacket packet, string port)
        {
            if (channel.ConnectionManager.EnsureConnection(port, true))
            {
                channel.ConnectionManager.ConnectionPool.Send(port, packet, false);
            }
            else
            {
                OnLostConnection(new EventArgs<_OutPacket>(packet));
            }
        }
        #endregion

        #region events

        /// <summary>
        /// 收到未知包
        /// </summary>
        public event EventHandler<EventArgs<UnknownRequestPacket>> ReceivedUnknownPacket;
        /// <summary>
        /// Raises the <see cref="E:ReceivedUnknownPacket"/> event.
        /// </summary>
        /// <param name="e">The instance containing the event data.</param>
        internal void OnReceivedUnknownPacket(EventArgs<UnknownRequestPacket> e)
        {
            if (ReceivedUnknownPacket != null)
            {
                ReceivedUnknownPacket(this.channel, e);
            }
        }
        /// <summary>
        /// 当一个包向服务器发送成功，并且收到服务器确认后
        /// </summary>
        public event EventHandler<EventArgs<_OutPacket, _InPacket>> SentPacketSuccessfully;
        /// <summary>
        /// Raises the <see cref="E:SendedPacketSuccess"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.OutPacket&gt;"/> instance containing the event data.</param>
        internal void OnSentPacketSuccessfully(EventArgs<_OutPacket, _InPacket> e)
        {
            if (SentPacketSuccessfully != null)
            {
                SentPacketSuccessfully(this.channel, e);
            }
        }
        /// <summary>
        /// Occurs when [send packet time out].包发送超时事件 InPacket为null
        /// </summary>
        public event EventHandler<EventArgs<_OutPacket>> SendPacketTimeOut;
        /// <summary>
        /// Raises the <see cref="E:SendPacketTimeOut"/> event.
        /// </summary>
        /// <param name="e">The instance containing the event data.</param>
        internal void OnSendPacketTimeOut(EventArgs<_OutPacket> e)
        {
            if (SendPacketTimeOut != null)
            {
                SendPacketTimeOut(this.channel, e);
            }
        }
        /// <summary>
        /// 无法得到有效的网络连接来发送包
        /// </summary>
        public event EventHandler<EventArgs<_OutPacket>> LostConnection;
        /// <summary>
        /// Raises the <see cref="E:LostConnection"/> event.
        /// </summary>
        /// <param name="e">The instance containing the event data.</param>
        internal void OnLostConnection(EventArgs<_OutPacket> e)
        {
            if (LostConnection != null)
            {
                LostConnection(this.channel, e);
            }
        }
        #endregion

    }
}
